#ifndef CONF_COLLECTION_H_
#define CONF_COLLECTION_H_

#include "element.h"

namespace gpyp {
namespace conf {

class CollectionBase : public Element {
public:
  virtual ElementList get_items() const = 0;

protected:
  inline CollectionBase(const Glib::ustring &name) :
    Element(name)
  {
  }
};
typedef boost::shared_ptr<CollectionBase> RefCollectionBase;

template<typename ItemType, typename DerivedClass>
class Collection : public CollectionBase {
public:
  typedef boost::shared_ptr<ItemType> RefItem;
  typedef std::list<RefItem> ItemList;
  typedef typename ItemList::iterator iterator;
  typedef typename ItemList::const_iterator const_iterator;

private:
  sigc::signal<void, DerivedClass &> signal_load_default_;
  sigc::signal<void, RefItem> signal_item_added_;
  sigc::signal<void, RefItem> signal_item_removed_;
  sigc::signal<void, RefItem, RefItem, RefItem> signal_item_reordered_;
  sigc::signal<void> signal_cleared_;
public:
  sigc::signal<void, DerivedClass &> signal_load_default() {
    return signal_load_default_;
  }
  sigc::signal<void, RefItem> signal_item_added() {
    return signal_item_added_;
  }
  sigc::signal<void, RefItem> signal_item_removed() {
    return signal_item_removed_;
  }
  sigc::signal<void, RefItem, RefItem, RefItem> signal_item_reordered() {
    return signal_item_reordered_;
  }
  sigc::signal<void> signal_cleared() {
    return signal_cleared_;
  }

private:
  Glib::ustring item_name_;
  ItemList      items_;
  bool          dirty_;

protected:
  virtual void on_add_item(RefItem item) {
    items_.push_back(item);
    set_dirty(true);
    signal_item_added_(item);
  }
  virtual void on_remove_item(iterator iter, RefItem item) {
    items_.erase(iter);
    set_dirty(true);
    signal_item_removed_(item);
  }
  virtual void on_reorder_item(RefItem item, RefItem prev, RefItem next) {
    iterator iter  = std::find(items_.begin(), items_.end(), item);
    iterator piter = std::find(items_.begin(), items_.end(), prev);
    iterator niter = std::find(items_.begin(), items_.end(), next);
    if (iter != items_.end()) {
      items_.erase(iter);
      if (niter != items_.end()) {
        iter = items_.insert(niter, item);
        signal_item_reordered_(item,
                               iter != begin() ? *--iter : RefItem(),
                               next);
      } else {
        iterator _piter = piter;
        iter = items_.insert(++_piter, item);
        signal_item_reordered_(item,
                               prev,
                               ++iter != end() ? *iter : RefItem());
      }
      set_dirty(true);
    }
  }
  virtual void on_clear() {
    items_.clear();
    set_dirty(true);
    signal_cleared_();
  }

  virtual void on_preload() {
    Element::on_preload();
    on_clear();
    set_dirty(false);
  }
  virtual void on_load(ConfParser  &parser,
                       const char  *element_name,
                       const char **attribute_names,
                       const char **attribute_values) {
    Element::on_load(parser, element_name, attribute_names, attribute_values);
    set_dirty(true);
  }
  virtual void on_start_element(ConfParser  &parser,
                                const char  *element_name,
                                const char **attribute_names,
                                const char **attribute_values,
                                GError     **error) {
    if (get_element_depth() == 0 &&
        (item_name_.empty() || item_name_ == element_name)) {
      RefItem item = on_create_item(element_name);
      parser.push(item.get(), element_name,
                  attribute_names, attribute_values);
      on_add_item(item);
      return;
    }
    Element::on_start_element(parser, element_name,
                              attribute_names, attribute_values, error);
  }
  virtual void on_postload() {
    Element::on_postload();
    for(iterator iter = begin(); iter != end(); ++iter) {
      (*iter)->on_postload();
    }
  }
  virtual void on_load_default() {
    Element::on_load_default();
    on_clear();
    signal_load_default_(*dynamic_cast<DerivedClass *>(this));
  }
  virtual void on_default_loaded() {
    Element::on_default_loaded();
    set_dirty(false, true);
  }

  virtual void on_presave() {
    Element::on_presave();
    for(iterator iter = items_.begin();
        iter != items_.end(); ++iter) {
      (*iter)->on_presave();
    }
  }
  virtual void on_save_contents(Writer &writer, bool force) {
    for(iterator iter = begin(); iter != end(); ++iter) {
      (*iter)->on_save(writer, true);
      writer.new_line();
    }
    Element::on_save_contents(writer, force);
  }
  virtual bool on_get_has_contents(bool force) const {
    /* この関数が呼ばれる時点でdirtyなので
       子リストが空でない限りtrueを返さなければならない。 */
    return Element::on_get_has_contents(force) || !items_.empty();
  }

  virtual bool on_get_dirty() const {
    if (dirty_ || Element::on_get_dirty()) return true;
    for(const_iterator iter = begin(); iter != end(); ++iter) {
      if ((*iter)->get_dirty()) return true;
    }
    return false;
  }
  virtual void on_set_dirty(bool dirty) {
    Element::on_set_dirty(dirty);
    dirty_ = dirty;
    for(iterator iter = begin(); iter != end(); ++iter) {
      (*iter)->on_set_dirty(dirty);
    }
  }

  virtual RefItem on_create_item(const Glib::ustring &item_name) = 0;

  void set_dirty(bool dirty, bool recursive = false) {
    dirty_ = dirty;
    if (recursive) on_set_dirty(dirty);
  }

public:
  inline iterator begin() {
    return items_.begin();
  }
  inline const_iterator begin() const {
    return items_.begin();
  }
  inline iterator end() {
    return items_.end();
  }
  inline const_iterator end() const {
    return items_.end();
  }
  inline RefItem front() {
    return items_.front();
  }
  inline RefItem back() {
    return items_.back();
  }

  inline size_t size() const {
    return items_.size();
  }

  RefItem add_item() {
    RefItem item = on_create_item(item_name_);
    on_add_item(item);
    return item;
  }
  void remove_item(RefItem item) {
    iterator iter = std::find(items_.begin(), items_.end(), item);
    if (iter != end()) {
      on_remove_item(iter, item);
    }
  }
  void reorder_item(RefItem item, RefItem prev, RefItem next) {
    if (has_item(item) &&
        (has_item(prev) || has_item(next))) {
      on_reorder_item(item, prev, next);
    }
  }
  void reorder_items(std::list<iterator> order) {
    order.unique();
    if (order.size() != items_.size()) {
      return;
    }
    for(typename std::list<iterator>::iterator iteriter = order.begin();
        iteriter != order.end(); ++iteriter) {
      iterator iter = *iteriter;
      typename std::list<iterator>::iterator _iteriter = iteriter;
      if (iteriter == order.begin()) {
        if (iter != begin()) {
          reorder_item(*iter, RefItem(), *begin());
        }
      } else {
        iterator piter = iter;
        if (*--piter != **--_iteriter) {
          reorder_item(*iter, **_iteriter, RefItem());
        }
      }
    }
  }

  bool has_item(RefItem item) {
    return std::find(items_.begin(), items_.end(), item) != items_.end();
  }

  virtual ElementList get_items() const {
    ElementList res;
    for(const_iterator iter = begin(); iter != end(); ++iter) {
      RefElement elem = boost::dynamic_pointer_cast<Element>(*iter);
      if (elem) res.push_back(elem);
    }
    return res;
  }

protected:
  Collection(const Glib::ustring &name,
             const Glib::ustring &item_name) :
    CollectionBase(name),
    item_name_    (item_name),
    dirty_        (false)
  {
  }
};

}/*conf*/
}/*gpyp*/

#endif/*CONF_COLLECTION_H_*/
